Prozkoumejte architekturu pluginů ve Vite a naučte se vytvářet vlastní pluginy pro vylepšení vašeho vývojového workflow. Osvojte si klíčové koncepty s praktickými příklady.
Demystifikace architektury pluginů ve Vite: Globální průvodce tvorbou vlastních pluginů
Vite, bleskově rychlý buildovací nástroj, způsobil revoluci ve vývoji frontendu. Jeho rychlost a jednoduchost jsou z velké části dány jeho výkonnou architekturou pluginů. Tato architektura umožňuje vývojářům rozšiřovat funkcionalitu Vite a přizpůsobovat ji specifickým potřebám jejich projektů. Tento průvodce poskytuje komplexní pohled na systém pluginů ve Vite, který vám umožní vytvářet vlastní pluginy a optimalizovat váš vývojový pracovní postup.
Pochopení klíčových principů Vite
Než se pustíme do tvorby pluginů, je nezbytné pochopit základní principy Vite:
- Kompilace na vyžádání (On-Demand Compilation): Vite kompiluje kód pouze tehdy, když si ho vyžádá prohlížeč, což výrazně zkracuje dobu spuštění.
- Nativní ESM: Vite pro vývoj využívá nativní ECMAScript moduly (ESM), čímž odpadá nutnost bundlování během vývoje.
- Produkční build založený na Rollupu: Pro produkční buildy Vite využívá Rollup, vysoce optimalizovaný bundler, k generování efektivního a produkčně připraveného kódu.
Role pluginů v ekosystému Vite
Architektura pluginů ve Vite je navržena tak, aby byla vysoce rozšiřitelná. Pluginy mohou:
- Transformovat kód (např. transpiling TypeScriptu, přidávání preprocesorů).
- Poskytovat vlastní soubory (např. zpracování statických aktiv, vytváření virtuálních modulů).
- Upravovat proces sestavení (např. optimalizace obrázků, generování service workerů).
- Rozšiřovat CLI Vite (např. přidávání vlastních příkazů).
Pluginy jsou klíčem k přizpůsobení Vite různým požadavkům projektu, od jednoduchých úprav po složité integrace.
Architektura pluginů ve Vite: Hloubkový pohled
Vite plugin je v podstatě JavaScriptový objekt se specifickými vlastnostmi, které definují jeho chování. Podívejme se na klíčové prvky:
Konfigurace pluginu
Soubor `vite.config.js` (nebo `vite.config.ts`) je místo, kde konfigurujete svůj Vite projekt, včetně specifikace, které pluginy se mají použít. Možnost `plugins` přijímá pole objektů pluginů nebo funkcí, které vracejí objekty pluginů.
// vite.config.js
import myPlugin from './my-plugin';
export default {
plugins: [
myPlugin(), // Zavolání funkce pluginu pro vytvoření instance pluginu
],
};
Vlastnosti objektu pluginu
Objekt Vite pluginu může mít několik vlastností, které definují jeho chování během různých fází procesu sestavení. Zde je přehled nejběžnějších vlastností:
- name: Unikátní název pluginu. Je vyžadován a pomáhá při ladění a řešení konfliktů. Příklad: `'my-custom-plugin'`
- enforce: Určuje pořadí spuštění pluginu. Možné hodnoty jsou `'pre'` (spustí se před základními pluginy), `'normal'` (výchozí) a `'post'` (spustí se po základních pluginech). Příklad: `'pre'`
- config: Umožňuje upravit konfigurační objekt Vite. Přijímá uživatelskou konfiguraci a prostředí (režim a příkaz). Příklad: `config: (config, { mode, command }) => { ... }`
- configResolved: Volá se poté, co je konfigurace Vite plně vyřešena. Užitečné pro přístup k finálnímu konfiguračnímu objektu. Příklad: `configResolved(config) { ... }`
- configureServer: Poskytuje přístup k instanci vývojového serveru (podobně jako Connect/Express). Užitečné pro přidávání vlastního middleware nebo úpravu chování serveru. Příklad: `configureServer(server) { ... }`
- transformIndexHtml: Umožňuje transformovat soubor `index.html`. Užitečné pro vkládání skriptů, stylů nebo meta tagů. Příklad: `transformIndexHtml(html) { ... }`
- resolveId: Umožňuje zachytit a upravit řešení modulů. Užitečné pro vlastní logiku řešení modulů. Příklad: `resolveId(source, importer) { ... }`
- load: Umožňuje načítat vlastní moduly nebo upravovat obsah existujících modulů. Užitečné pro virtuální moduly nebo vlastní loadery. Příklad: `load(id) { ... }`
- transform: Transformuje zdrojový kód modulů. Podobné jako Babel plugin nebo PostCSS plugin. Příklad: `transform(code, id) { ... }`
- buildStart: Volá se na začátku procesu sestavení. Příklad: `buildStart() { ... }`
- buildEnd: Volá se po dokončení procesu sestavení. Příklad: `buildEnd() { ... }`
- closeBundle: Volá se poté, co je balíček zapsán na disk. Příklad: `closeBundle() { ... }`
- writeBundle: Volá se před zápisem balíčku na disk, umožňuje úpravu. Příklad: `writeBundle(options, bundle) { ... }`
- renderError: Umožňuje vykreslit vlastní chybové stránky během vývoje. Příklad: `renderError(error, req, res) { ... }`
- handleHotUpdate: Umožňuje jemně zrnitou kontrolu nad HMR. Příklad: `handleHotUpdate({ file, server }) { ... }`
Hooky pluginu a pořadí spuštění
Vite pluginy fungují prostřednictvím série hooků, které se spouštějí v různých fázích procesu sestavení. Pochopení pořadí, v jakém se tyto hooky spouštějí, je klíčové pro psaní efektivních pluginů.
- config: Upraví konfiguraci Vite.
- configResolved: Získá přístup k vyřešené konfiguraci.
- configureServer: Upraví vývojový server (pouze při vývoji).
- transformIndexHtml: Transformuje soubor `index.html`.
- buildStart: Začátek procesu sestavení.
- resolveId: Vyřeší ID modulů.
- load: Načte obsah modulu.
- transform: Transformuje kód modulu.
- handleHotUpdate: Zpracuje Hot Module Replacement (HMR).
- writeBundle: Upraví výstupní balíček před zápisem na disk.
- closeBundle: Volá se poté, co byl výstupní balíček zapsán na disk.
- buildEnd: Konec procesu sestavení.
Vytvoření vašeho prvního vlastního Vite pluginu
Vytvořme si jednoduchý Vite plugin, který přidá banner na začátek každého JavaScriptového souboru v produkčním buildu. Tento banner bude obsahovat název a verzi projektu.
Implementace pluginu
// banner-plugin.js
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
export default function bannerPlugin() {
return {
name: 'banner-plugin',
apply: 'build',
transform(code, id) {
if (!id.endsWith('.js')) {
return code;
}
const packageJsonPath = resolve(process.cwd(), 'package.json');
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
const banner = `/**\n * Project: ${packageJson.name}\n * Version: ${packageJson.version}\n */\n`;
return banner + code;
},
};
}
Vysvětlení:
- name: Definuje název pluginu, 'banner-plugin'.
- apply: Určuje, že tento plugin by se měl spouštět pouze během procesu sestavení (build). Nastavení na 'build' ho činí produkčním, čímž se zabrání zbytečnému zatížení během vývoje.
- transform(code, id):
- Toto je jádro pluginu. Zachycuje kód (`code`) a ID (`id`) každého modulu.
- Podmíněná kontrola: `if (!id.endsWith('.js'))` zajišťuje, že transformace se aplikuje pouze na JavaScriptové soubory. Tím se zabrání zpracování jiných typů souborů (jako CSS nebo HTML), což by mohlo způsobit chyby nebo neočekávané chování.
- Přístup k package.json:
- `resolve(process.cwd(), 'package.json')` vytvoří absolutní cestu k souboru `package.json`. `process.cwd()` vrací aktuální pracovní adresář, což zaručuje použití správné cesty bez ohledu na to, odkud je příkaz spuštěn.
- `JSON.parse(readFileSync(packageJsonPath, 'utf-8'))` čte a parsuje soubor `package.json`. `readFileSync` čte soubor synchronně a `'utf-8'` specifikuje kódování pro správné zpracování Unicode znaků. Synchronní čtení je zde přijatelné, protože probíhá jednou na začátku transformace.
- Generování banneru:
- ``const banner = `/**\n * Project: ${packageJson.name}\n * Version: ${packageJson.version}\n */\n`;`` vytváří řetězec banneru. Používá šablonové literály (zpětné apostrofy) pro snadné vložení názvu a verze projektu ze souboru `package.json`. Sekvence `\n` vkládají nové řádky pro správné formátování banneru. `*` je escapována jako `\*`.
- Transformace kódu: `return banner + code;` připojí banner na začátek původního JavaScriptového kódu. Toto je konečný výsledek vrácený transformační funkcí.
Integrace pluginu
Importujte plugin do vašeho souboru `vite.config.js` a přidejte ho do pole `plugins`:
// vite.config.js
import bannerPlugin from './banner-plugin';
export default {
plugins: [
bannerPlugin(),
],
};
Spuštění buildu
Nyní spusťte `npm run build` (nebo příkaz pro sestavení vašeho projektu). Po dokončení buildu zkontrolujte vygenerované JavaScriptové soubory v adresáři `dist`. Uvidíte banner na začátku každého souboru.
Pokročilé techniky pluginů
Kromě jednoduchých transformací kódu mohou Vite pluginy využívat pokročilejší techniky k vylepšení svých schopností.
Virtuální moduly
Virtuální moduly umožňují pluginům vytvářet moduly, které neexistují jako skutečné soubory na disku. To je užitečné pro generování dynamického obsahu nebo poskytování konfiguračních dat aplikaci.
// virtual-module-plugin.js
export default function virtualModulePlugin(options) {
const virtualModuleId = 'virtual:my-module';
const resolvedVirtualModuleId = '\0' + virtualModuleId; // Prefix \0 zabrání Rollupu ve zpracování
return {
name: 'virtual-module-plugin',
resolveId(id) {
if (id === virtualModuleId) {
return resolvedVirtualModuleId;
}
},
load(id) {
if (id === resolvedVirtualModuleId) {
return `export default ${JSON.stringify(options)};`;
}
},
};
}
V tomto příkladu:
- `virtualModuleId` je řetězec, který představuje identifikátor virtuálního modulu.
- `resolvedVirtualModuleId` je prefixován `\0`, aby Rollup nezpracovával tento modul jako skutečný soubor. Toto je konvence používaná v Rollup pluginech.
- `resolveId` zachycuje řešení modulů a vrací vyřešené ID virtuálního modulu, pokud se požadované ID shoduje s `virtualModuleId`.
- `load` zachycuje načítání modulů a vrací kód modulu, pokud se požadované ID shoduje s `resolvedVirtualModuleId`. V tomto případě generuje JavaScriptový modul, který exportuje `options` jako výchozí export.
Použití virtuálního modulu
// vite.config.js
import virtualModulePlugin from './virtual-module-plugin';
export default {
plugins: [
virtualModulePlugin({ message: 'Hello from virtual module!' }),
],
};
// main.js
import message from 'virtual:my-module';
console.log(message.message); // Výstup: Hello from virtual module!
Transformace souboru index.html
Hook `transformIndexHtml` umožňuje upravovat soubor `index.html`, například vkládat skripty, styly nebo meta tagy. To je užitečné pro přidávání analytického sledování, konfiguraci metadat pro sociální média nebo přizpůsobení struktury HTML.
// inject-script-plugin.js
export default function injectScriptPlugin() {
return {
name: 'inject-script-plugin',
transformIndexHtml(html) {
return html.replace(
'